#include <UnitTest++.h>
#include "..\trigger_state.h"
#include "trigger_state_fixtures.h"
#include <stdarg.h>
#include <algorithm>
#include <iterator>
#include <cmath>

TEST_FIXTURE(SineWaveFixture, PreTrigChangesState)
{
    arrayHolder dh(GROUPBYSCAN, 40, 1, y);
    ChanPointer cp(dh, 0);
    TriggerContext tc(11, 10, RISING, 0.0, 0.0, cp, 1, 40);
    CHECK_EQUAL(tc.tsFilling, *tc.pState->update(5));
    CHECK_EQUAL(tc.tsFilling, *tc.pState->update(5));
    CHECK_EQUAL(tc.tsArmed, *tc.pState->update(5));
}

TEST_FIXTURE(SineWaveFixture, ArmingUsesIndex)
{
    arrayHolder dh(GROUPBYSCAN, 40, 1, y);
    ChanPointer cp(dh, 0);
    TriggerContext tc(0, 10, RISING, -3.0, -3.0, cp, 1, 40);
    TriggerState* pTS  = &tc.tsFull;
    CHECK_EQUAL(tc.tsFull, *pTS->updateInstance(6, 3));
    CHECK_EQUAL(tc.tsDone, *pTS->update(6));
}

TEST_FIXTURE(TriggerFixture, StartToFinish)
//! Almost certainly should not use pointer to starting state more than once as
//! it is not way class is intended to be used and tests fail at hire
//! optimization levels.
{
    CHECK_EQUAL(tsFilling, *(pTS = pTS->update(0)));
//    std::cout << "spot one: " << *temp << " and pTS = " << *pTS <<std::endl;
    CHECK_EQUAL(tsFilling, *(pTS = pTS->update(2)));
//    std::cout << "spot two: " << *temp << " and pTS = " << *pTS <<std::endl;
    pTS = pTS->update(3);
    CHECK_EQUAL(tsArmed, *pTS);
//    std::cout << "spot three: " << *temp << " and pTS = " << *pTS <<std::endl;
    pTS = pTS->update(11);                //  temp = temp->update(11) // passes on -O2!
    CHECK_EQUAL(tsDone, *pTS);
//    std::cout << "spot four: " << *temp << " and pTS = " << *pTS <<std::endl;
//    CHECK_EQUAL(8, temp->tempIndex);
}
TEST_FIXTURE(TriggerFixture, PassesOnOptimizationLevel_02)
{
    // This test fails when part of Test_fixture above and
    // optimaization level -02 ???
    CHECK_EQUAL(tsDone, *pTS->update(11));
}


TEST(TriggerContext)
{
    double fakeData[11] = {-5,-4,-3,-2,-1, 0, 1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 11, 1, fakeData);
    ChanPointer cp(dh, 0);
    TriggerContext tc(3, 10, RISING, 2.5, 2.5, cp, 1, 11);
    CHECK_EQUAL(tc.tsFilling, *tc.pState);
//    std::cout << *tc.pState <<std::endl;
    tc.pState = tc.pState->update(5);
    CHECK_EQUAL(tc.tsArmed, *tc.pState);
    CHECK_EQUAL(tc.tsDone, *tc.pState->update(11));  // fails at -03 works at -02
    CHECK_EQUAL(8, tc.pState->getTempIndex());
}

TEST_FIXTURE(SineWaveFixture, TestRisingWithPreTrigger)
{
    arrayHolder dh(GROUPBYSCAN, 40, 1, y);
    ChanPointer cp(dh, 0);
    TriggerContext tc1(5, 10, RISING, 0.0, 0.0, cp, 1, 40);
    CHECK_EQUAL(14, tc1.pState->update(40)->getTempIndex());
    TriggerContext tc2(5, 10, RISING, 2.5, 2.5, cp, 1, 40);
    CHECK_EQUAL(26, tc2.pState->update(40)->getTempIndex());
    TriggerContext tc3(15, 10, RISING, 0.0, 0.0, cp, 1, 40);
    CHECK_EQUAL(25, tc3.pState->update(40)->getTempIndex());
    TriggerContext tc4(0, 10, RISING, -3.0, -3.0, cp, 1, 40);
    CHECK_EQUAL(2, tc4.pState->update(40)->getTempIndex());
    TriggerContext tc5(2, 10, RISING, -3.0, -3.0, cp, 1, 40);
    CHECK_EQUAL(12, tc5.pState->update(40)->getTempIndex());
}

TEST_FIXTURE(SineWaveFixture, TestFallingWithPreTrigger)
{
    arrayHolder dh(GROUPBYSCAN, 40, 1, y);
    ChanPointer cp(dh, 0);
    long pre=0, post=10, slope=FALLING; double voltageLevel=0.0, trigLevel = 0.0;
    int numChans=1; long bufSize = 40;
    TriggerContext tc1(pre, post, slope, voltageLevel, trigLevel, cp, numChans, bufSize);
    CHECK_EQUAL(19, tc1.pState->update(40)->getTempIndex());
    TriggerContext tc2(6, 10, FALLING, -2.0, -2.0, cp, 1, 40);
    CHECK_EQUAL(21, tc2.pState->update(40)->getTempIndex());
    TriggerContext tc3(20, 10, FALLING, 1.0, 1.0, cp, 1, 40);
    CHECK_EQUAL(34, tc3.pState->update(40)->getTempIndex());
    TriggerContext tc4(6, 10, FALLING, -2.0, -2.0, cp, 1, 40);
    CHECK_EQUAL(21, tc4.pState->update(40)->getTempIndex());
}



TEST(ChanPointerOperators)
{
    double buf[]= {0,1,2,3,4,5,6,7,8,9,10,11};
    arrayHolder dhScan(GROUPBYSCAN, 40, 4, buf);
    ChanPointer p0(dhScan, 0);
    ChanPointer pZero(dhScan, 0);
    ChanPointer p1(dhScan, 1);
    CHECK(p0 == p0);
    CHECK(p0 == pZero);
    CHECK(p0 == p0);
    CHECK(p0 != p1);
    CHECK(*++p0 == pZero[1]);
    CHECK(pZero < p0);
    CHECK_EQUAL(1, p0 - pZero);

}

TEST_FIXTURE(SineWaveFixture, RisingIncrementalData)
{
//    double buf[40];
    arrayHolder dh(GROUPBYSCAN, 40, 1);
    ChanPointer cp(dh, 0);

    TriggerContext tc(15, 10, RISING, 0.0, 0.0, cp, 1, 40);
        long samplesWritten = 0;
    TriggerState* pTS = tc.pState;
    long chunkSize = 7;

    for(double  *first = y, *last = NULL, *final = y + sizeof(y)/sizeof(y[0]);
        last = (first + chunkSize < final ? first+chunkSize : final), std::copy(first, last, dh.getData()), samplesWritten = last - first, first != final;
        first = last)
    {
        pTS = pTS->update(samplesWritten);
        if(false)
        {
            std::cout << samplesWritten  << " : ";
            for(long i=0; i<samplesWritten;i++)
                std::cout  << first[i] << "--->" ;
            std::cout << *pTS << " index = ";
            std::cout << pTS->getTempIndex() <<std::endl;
        }
        if (*pTS == tc.tsDone)
            break;
    }
    CHECK_EQUAL(25 % chunkSize, pTS->getTempIndex());

//    CHECK_EQUAL(25, tc3.pState->update(40)->tempIndex);
}
